use actix_web::{web, HttpResponse, Responder};
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager};
use serde::{Deserialize, Serialize};
use crate::db::models::{NewUser, User};
use crate::db::{self, schema::users};
use bcrypt::{hash, DEFAULT_COST};
use jsonwebtoken::{encode, EncodingKey, Header};
use chrono::{Utc, Duration};

#[derive(Deserialize)]
pub struct RegisterRequest {
    username: String,
    email: String,
    password: String,
}

#[derive(Serialize)]
pub struct RegisterResponse {
    id: i32,
    username: String,
    email: String,
}

pub async fn register(
    pool: web::Data<db::DbPool>,
    form: web::Json<RegisterRequest>,
) -> impl Responder {
    let conn = pool.get().expect("Couldn't get db connection from pool");

    let new_user = NewUser {
        username: &form.username,
        email: &form.email,
        password_hash: &hash_password(&form.password),
    };

    let user = web::block(move || {
        diesel::insert_into(users::table)
            .values(&new_user)
            .get_result::<User>(&conn)
    })
    .await
    .map_err(|e| {
        eprintln!("{}", e);
        HttpResponse::InternalServerError().finish()
    })?;

    Ok(HttpResponse::Ok().json(RegisterResponse {
        id: user.id,
        username: user.username,
        email: user.email,
    }))
}

fn hash_password(password: &str) -> String {
    hash(password, DEFAULT_COST).unwrap()
}

#[derive(Deserialize)]
pub struct LoginRequest {
    username: String,
    password: String,
}

#[derive(Serialize)]
pub struct LoginResponse {
    token: String,
}

pub async fn login(
    pool: web::Data<db::DbPool>,
    form: web::Json<LoginRequest>,
) -> impl Responder {
    let conn = pool.get().expect("Couldn't get db connection from pool");

    let user = web::block(move || {
        users::table
            .filter(users::username.eq(&form.username))
            .first::<User>(&conn)
    })
    .await
    .map_err(|e| {
        eprintln!("{}", e);
        HttpResponse::InternalServerError().finish()
    })?;

    if verify_password(&form.password, &user.password_hash) {
        let token = create_jwt(&user.id)?;
        Ok(HttpResponse::Ok().json(LoginResponse { token }))
    } else {
        Ok(HttpResponse::Unauthorized().json("Invalid username or password"))
    }
}

fn verify_password(password: &str, password_hash: &str) -> bool {
    bcrypt::verify(password, password_hash).unwrap()
}

fn create_jwt(user_id: &i32) -> Result<String, HttpResponse> {
    let claims = Claims {
        sub: user_id.to_string(),
        exp: (Utc::now() + Duration::hours(24)).timestamp(),
    };
    let token = encode(
        &Header::default(),
        &claims,
        &EncodingKey::from_secret("your-256-bit-secret".as_ref()),
    )
    .map_err(|e| {
        eprintln!("{}", e);
        HttpResponse::InternalServerError().finish()
    })?;
    Ok(token)
}

#[derive(Serialize, Deserialize)]
struct Claims {
    sub: String,
    exp: i64,
}
